#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using XNAPlatformer.Source.Characters;
using XNAPlatformer.Source.Levels;
using XNAPlatformer.Source.Weapons;

#endregion

namespace XNAPlatformer
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class XNAPlatformer : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        ContentManager content;
        SpriteBatch spritebatch;

        protected enum GameStates { TitleScreenReady, TitleScreen, InGameReady, InGame };
        protected GameStates gameState;

        Player player; //probably a list or array of two players (??or more??)
        public static Level currentLevel;
        public static List<Bullet> activeBullets;
        public static List<Bullet> enemyBullets;
        public static List<Enemy> enemies;
        protected List<Enemy> removeDeadEnemies;
        protected List<Bullet> removeDeadBullets;



        public XNAPlatformer()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 640;
            graphics.PreferredBackBufferHeight = 480;
            content = new ContentManager(Services);
        }


        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            spritebatch = new SpriteBatch(graphics.GraphicsDevice);
            gameState = GameStates.InGameReady;
            base.Initialize();
        }

        protected void InitializeNewGame()
        {
            player = new Player(content, new Vector2(32, 100));
            currentLevel = new Level(content);
            activeBullets = new List<Bullet>();
            enemies = new List<Enemy>();
            enemyBullets = new List<Bullet>();
            removeDeadEnemies = new List<Enemy>();
            removeDeadBullets = new List<Bullet>();
        }


        /// <summary>
        /// Load your graphics content.  If loadAllContent is true, you should
        /// load content from both ResourceManagementMode pools.  Otherwise, just
        /// load ResourceManagementMode.Manual content.
        /// </summary>
        /// <param name="loadAllContent">Which type of content to load.</param>
        protected override void LoadGraphicsContent(bool loadAllContent)
        {
            if (loadAllContent)
            {
                // TODO: Load any ResourceManagementMode.Automatic content
            }

            // TODO: Load any ResourceManagementMode.Manual content
        }


        /// <summary>
        /// Unload your graphics content.  If unloadAllContent is true, you should
        /// unload content from both ResourceManagementMode pools.  Otherwise, just
        /// unload ResourceManagementMode.Manual content.  Manual content will get
        /// Disposed by the GraphicsDevice during a Reset.
        /// </summary>
        /// <param name="unloadAllContent">Which type of content to unload.</param>
        protected override void UnloadGraphicsContent(bool unloadAllContent)
        {
            if (unloadAllContent)
            {
                // TODO: Unload any ResourceManagementMode.Automatic content
                content.Unload();
            }

            // TODO: Unload any ResourceManagementMode.Manual content
        }


        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            GamePadState gamePad = GamePad.GetState(PlayerIndex.One);

            switch (gameState)
            {
                case GameStates.InGameReady:
                    //queue in game music
                    //display loading screen;
                    InitializeNewGame();
                    gameState = GameStates.InGame;
                    break;

                case GameStates.InGame:

                    // Allows the game to exit
                    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                        this.Exit();

                    player.Update(gameTime, gamePad);

                    //if player is dead, lose a life, if all lives are gone, gameover

                    currentLevel.Update(gameTime, gamePad, player.MoveMap);

                    removeDeadBullets = new List<Bullet>();

                    foreach (Bullet bullet in activeBullets)
                    {
                        bullet.Update(gameTime);

                        if (bullet.IsDead)
                        {
                            removeDeadBullets.Add(bullet);
                        }
                    }


                    removeDeadEnemies = new List<Enemy>();

                    foreach (Enemy enemy in enemies)
                    {
                        enemy.Update(gameTime);

                        if (enemy.IsDead)
                        {
                            removeDeadEnemies.Add(enemy);
                        }
                    }
                    

                    

                    //remove dead bullets and enemies
                    foreach (Bullet bullet in removeDeadBullets)
                    {
                        activeBullets.Remove(bullet);
                    }

                    foreach (Enemy enemy in removeDeadEnemies)
                    {
                        enemies.Remove(enemy);
                    }
                    break;
            }

            base.Update(gameTime);

        }


        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.Black);
            spritebatch.Begin();
            switch (gameState)
            {

                case GameStates.InGameReady:
                    //display loading screen;
                    break;

                case GameStates.InGame:


                    currentLevel.Draw(gameTime, spritebatch);
                    
                    player.Draw(gameTime, spritebatch);

                    foreach (Enemy enemy in enemies)
                    {
                        enemy.Draw(gameTime, spritebatch);
                    }

                    foreach (Bullet bullet in activeBullets)
                    {
                        bullet.Draw(gameTime, spritebatch);
                    }

                    foreach (Bullet bullet in enemyBullets)
                    {
                        bullet.Draw(gameTime, spritebatch);
                    }
                    break;
            }

            spritebatch.End();

            base.Draw(gameTime);
        }

        public static bool IntersectPixels(Rectangle rectangleA, Color[] dataA,
                                   Rectangle rectangleB, Color[] dataB)
        {
            // Find the bounds of the rectangle intersection
            int top = Math.Max(rectangleA.Top, rectangleB.Top);
            int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
            int left = Math.Max(rectangleA.Left, rectangleB.Left);
            int right = Math.Min(rectangleA.Right, rectangleB.Right);

            // Check every point within the intersection bounds
            for (int y = top; y < bottom; y++)
            {
                for (int x = left; x < right; x++)
                {
                    // Get the color of both pixels at this point
                    Color colorA = dataA[(x - rectangleA.Left) +
                                         (y - rectangleA.Top) * rectangleA.Width];
                    Color colorB = dataB[(x - rectangleB.Left) +
                                         (y - rectangleB.Top) * rectangleB.Width];

                    // If both pixels are not completely transparent,
                    if (colorA.A != 0 && colorB.A != 0)
                    {
                        // then an intersection has been found
                        return true;
                    }
                }
            }

            // No intersection found
            return false;
        }
    }
}
